﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Configuration;
using System.Reflection;

namespace ModuleSettings
{
    /// <summary>
    /// This class aims to manage a settings class as a module.
    /// </summary>
    public abstract class ModuleSettingsProviderBase : SettingsProvider, IApplicationSettingsProvider
    {
        #region fields

        /// <summary>
        /// Represents the name of the module to manage.
        /// </summary>
        private string _moduleName;

        /// <summary>
        /// Represents the repository instance which reads and writes the settings properties from Storage.
        /// </summary>
        private IUserSettingsRepository m_Repo;

        #endregion fields

        #region properties

        /// <summary>
        /// Gets and sets the name of the module to manage.
        /// </summary>
        public virtual string ModuleName
        {
            get
            {
                if (string.IsNullOrEmpty(_moduleName))
                    _moduleName = Assembly.GetCallingAssembly().GetName().Name;
                return _moduleName;
            }
            set
            {
                _moduleName = value;
            }
        }
        /// <summary>
        /// Gets or sets the data source for the settings module to store.
        /// </summary>
        public virtual string DataSource
        {
            get;
            set;
        }
        /// <summary>
        /// Gets the name of this SettingsProvider
        /// </summary>
        public override string Name
        {
            get
            {
                return "ModuleSettingsProvider";
            }
        }
        /// <summary>
        /// Gets or sets the repository instance to store the settings.
        /// </summary>
        protected virtual IUserSettingsRepository Repo
        {
            get
            {
                if (m_Repo == null)
                {
                    m_Repo = new XmlUserSettingsRepo();
                    m_Repo.Load(DataSource, ModuleName);
                }
                return m_Repo;
            }
            set
            {
                m_Repo = value;
                if (m_Repo != null)
                    m_Repo.Load(DataSource, ModuleName);
            }
        }

        #endregion properties

        #region methods

        #region IApplicationSettingsProvider Members

        public SettingsPropertyValue GetPreviousVersion(SettingsContext context, SettingsProperty property)
        {
            throw new NotImplementedException();
        }

        public void Reset(SettingsContext context)
        {
            ClearAll(DataSource, ModuleName);
        }

        public void Upgrade(SettingsContext context, SettingsPropertyCollection properties)
        {
            List<object[]> propToUpgrade = new List<object[]>();

            foreach (SettingsProperty prop in properties)
            {
                if (SettingsUtil.IsUserScopeSettings(prop))
                {
                    object value = GetPropertyValue(ModuleName, prop.Name);
                    if (value != null)
                        propToUpgrade.Add(new object[] { ModuleName, prop.Name, value});
                }
            }
            ClearAll(DataSource, ModuleName);

            foreach (var prop in propToUpgrade)
            {
                SetPropertyValue(prop[0] as string, prop[1] as string, prop[2] as object);
            }

            SaveAll(DataSource, ModuleName);
        }

        #endregion IApplicationSettingsProvider Members

        #region SettingsProvider Members

        public override string ApplicationName
        {
            get
            {
                return ModuleName;
            }
            set
            {
            }
        }

        public override SettingsPropertyValueCollection GetPropertyValues(SettingsContext context, SettingsPropertyCollection properties)
        {
            SettingsPropertyValueCollection spvc = new SettingsPropertyValueCollection();
            foreach (SettingsProperty sp in properties)
            {
                SettingsPropertyValue spv = new SettingsPropertyValue(sp);
                object value = GetPropertyValue(ModuleName, spv.Name);
                spv.SerializedValue = value == null ? sp.DefaultValue : value;
                spv.IsDirty = false;
                spvc.Add(spv);
            }
            return spvc;
        }

        public override void SetPropertyValues(SettingsContext context, SettingsPropertyValueCollection values)
        {
            foreach (SettingsPropertyValue spv in values)
            {
                if (!spv.IsDirty) continue;
                SetPropertyValue(ModuleName, spv.Name, spv.SerializedValue);
                spv.IsDirty = false;
            }
            SaveAll(DataSource, ModuleName);
        }

        #endregion SettingsProvider Members

        /// <summary>
        /// Resets the given properties to the default values.
        /// </summary>
        /// <param name="propNames">The names of the settings properties to reset.</param>
        public void Reset(params string[] propNames)
        {
            if (propNames == null || propNames.Length == 0) return;
            foreach (string propName in propNames)
                SetPropertyValue(ModuleName, propName, null);
        }

        /// <summary>
        /// Saves all changings.
        /// </summary>
        /// <param name="dataSource"></param>
        /// <param name="moduleName"></param>
        private void SaveAll(string dataSource, string moduleName)
        {
            Repo.Save();
        }

        /// <summary>
        /// Clears all changings and resets to default values.
        /// </summary>
        /// <param name="dataSource"></param>
        /// <param name="moduleName"></param>
        private void ClearAll(string dataSource, string moduleName)
        {
            IList<string> propNames = Repo.GetAllSettingNames();
            foreach (string prop in propNames)
            {
                Repo.SetSettingValue(prop, null);
            }
            Repo.Save();
        }

        /// <summary>
        /// Sets a new value to the given property.
        /// </summary>
        /// <param name="module"></param>
        /// <param name="propertyName"></param>
        /// <param name="value"></param>
        private void SetPropertyValue(string module, string propertyName, object value)
        {
            Repo.SetSettingValue(propertyName, value as string);
        }
        
        /// <summary>
        /// Gets the current value of the given property.
        /// </summary>
        /// <param name="module"></param>
        /// <param name="propertyName"></param>
        /// <returns></returns>
        private object GetPropertyValue(string module, string propertyName)
        {
            return Repo.GetSettingValue(propertyName);
        }

        #endregion methods

    }
}
